﻿using System.Collections.Generic;
using System.Linq;
using CID.Domain.Development;
using CID.Domain.Exceptions;
using NailsFramework.Persistence;

namespace CID.Domain.Operations
{
    public class ServerRole : Model<ServerRole>, IServerRole
    {
        public virtual int Id { get; private set; }
        public virtual string Name { get; set; }
        public virtual string Description { get; set; }
        public virtual bool Enabled { get; set; }

        private IList<IComponent> components = new List<IComponent>();
        public virtual IEnumerable<IComponent> Components { get { return components; } }

        private IList<IServerAssignment> assignments = new List<IServerAssignment>();
        public virtual IEnumerable<IServerAssignment> Assignments { get { return assignments; } }

        protected ServerRole()
        {
            Enabled = true;
        }

        public ServerRole(string name, string description) : this()
        {
            Name = name;
            Description = description;
        }

        #region Equals and GetHashCode
        //This class needs to have the Equals overriden to support the comparison with the proxies from the persistence layer
        public override bool Equals(object obj)
        {
            return EntityEqualityHelper.Equals(this, obj);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
        #endregion

        public virtual void Assign(IServer server, IEnvironment environment)
        {
            var assignment = new ServerAssignment(server, this, environment);
            AddAssignment(assignment);
            server.AddAssignment(assignment);
            environment.AddAssignment(assignment);
        }

        public virtual void Unassign(IServer server, IEnvironment environment)
        {
            var assignment = Assignments.FirstOrDefault(x => (x.Environment.Equals(environment)) && (x.Server.Equals(server)));
            if (assignment != null)
            {
                RemoveAssignment(assignment);
                environment.RemoveAssignment(assignment);
                server.RemoveAssignment(assignment);
            }
        }

        public virtual void AddAssignment(IServerAssignment assignment)
        {
            if (!Equals(assignment.ServerRole)) throw new InvalidAssignmentException(this);

            if (Assignments.Count(x => (x.Environment.Equals(assignment.Environment)) && (x.Server.Equals(assignment.Server))) > 0)
                throw new DuplicateAssignmentException(this, assignment.Environment, assignment.Server);

            assignments.Add(assignment);
        }

        public virtual void RemoveAssignment(IServerAssignment assignment)
        {
            if (!Equals(assignment.ServerRole)) throw new InvalidAssignmentException(this);

            if (assignments.Contains(assignment)) assignments.Remove(assignment);
        }

        public virtual void AddComponent(IComponent component)
        {
            if (!components.Contains(component))
            {
                if (component.RequiredServerRole != null) component.RequiredServerRole.RemoveComponent(component);
                components.Add(component);
                component.RequiredServerRole = this;
            }
        }

        public virtual void RemoveComponent(IComponent component)
        {
            if (components.Contains(component))
            {
                components.Remove(component);
                component.RequiredServerRole = null;
            }
        }
    }
}
